// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX                                                                           XX
XX                          DisAsm                                           XX
XX                                                                           XX
XX  The dis-assembler to display the native code generated                   XX
XX                                                                           XX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

/*****************************************************************************/
#ifndef _DIS_H_
#define _DIS_H_
/*****************************************************************************/
#ifdef LATE_DISASM

// free() is deprecated (we should only allocate and free memory through CLR hosting interfaces)
// and is redefined in clrhost.h to cause a compiler error.
// We don't call free(), but this function is mentioned in STL headers included by msvcdis.h
// (and free() is only called by STL functions that we don't use).
// To avoid the compiler error, but at the same time ensure that we don't accidentally use free(),
// free() is redefined to cause a runtime error instead of a compile time error.
#undef free
#ifdef DEBUG
#define free(x) assert(false && "Must not call free(). Use a ClrXXX function instead.")
#endif

#if CHECK_STRUCT_PADDING
#pragma warning(pop)
#endif // CHECK_STRUCT_PADDING

#define _OLD_IOSTREAMS
// This pragma is needed because public\vc\inc\xiosbase contains
// a static local variable
#pragma warning(disable : 4640)
#include "msvcdis.h"
#pragma warning(default : 4640)

#ifdef _TARGET_XARCH_
#include "disx86.h"
#elif defined(_TARGET_ARM64_)
#include "disarm64.h"
#else // _TARGET_*
#error Unsupported or unset target architecture
#endif

#if CHECK_STRUCT_PADDING
#pragma warning(push)
#pragma warning(default : 4820) // 'bytes' bytes padding added after construct 'member_name'
#endif                          // CHECK_STRUCT_PADDING

/*****************************************************************************/

#ifdef _HOST_64BIT_
template <typename T>
struct SizeTKeyFuncs : LargePrimitiveKeyFuncs<T>
{
};
#else  // !_HOST_64BIT_
template <typename T>
struct SizeTKeyFuncs : SmallPrimitiveKeyFuncs<T>
{
};
#endif // _HOST_64BIT_

typedef SimplerHashTable<size_t, SizeTKeyFuncs<size_t>, CORINFO_METHOD_HANDLE, JitSimplerHashBehavior>
    AddrToMethodHandleMap;
typedef SimplerHashTable<size_t, SizeTKeyFuncs<size_t>, size_t, JitSimplerHashBehavior> AddrToAddrMap;

class Compiler;

class DisAssembler
{
public:
    // Constructor
    void disInit(Compiler* pComp);

    // Initialize the class for the current method being generated.
    void disOpenForLateDisAsm(const char* curMethodName, const char* curClassName, PCCOR_SIGNATURE sig);

    // Disassemble a buffer: called after code for a method is generated.
    void disAsmCode(BYTE* hotCodePtr, size_t hotCodeSize, BYTE* coldCodePtr, size_t coldCodeSize);

    // Register an address to be associated with a method handle.
    void disSetMethod(size_t addr, CORINFO_METHOD_HANDLE methHnd);

    // Register a relocation address.
    void disRecordRelocation(size_t relocAddr, size_t targetAddr);

private:
    /* Address of the hot and cold code blocks to dissasemble */
    size_t disHotCodeBlock;
    size_t disColdCodeBlock;

    /* Size of the hot and cold code blocks to dissasemble */
    size_t disHotCodeSize;
    size_t disColdCodeSize;

    /* Total code size (simply cached version of disHotCodeSize + disColdCodeSize) */
    size_t disTotalCodeSize;

    /* Address where the code block is to be loaded */
    size_t disStartAddr;

    /* Current offset in the code block */
    size_t disCurOffset;

    /* Size (in bytes) of current dissasembled instruction */
    size_t disInstSize;

    /* Target address of a jump */
    size_t disTarget;

    /* temporary buffer for function names */
    // TODO-Review: there is some issue here where this is never set!
    char disFuncTempBuf[1024];

    /* Method and class name to output */
    const char* disCurMethodName;
    const char* disCurClassName;

    /* flag that signals when replacing a symbol name has been deferred for following callbacks */
    // TODO-Review: there is some issue here where this is never set to 'true'!
    bool disHasName;

    /* An array of labels, for jumps, LEAs, etc. There is one element in the array for each byte in the generated code.
     * That byte is zero if the corresponding byte of generated code is not a label. Otherwise, the value
     * is a label number.
     */
    BYTE* disLabels;

    void DisasmBuffer(FILE* pfile, bool printit);

    /* For the purposes of disassembly, we pretend that the hot and cold sections are linear, and not split.
     * These functions create this model for the rest of the disassembly code.
     */

    /* Given a linear offset into the code, find a pointer to the actual code (either in the hot or cold section) */
    const BYTE* disGetLinearAddr(size_t offset);

    /* Given a linear offset into the code, determine how many bytes are left in the hot or cold buffer the offset
     * points to */
    size_t disGetBufferSize(size_t offset);

    // Map of instruction addresses to call target method handles for normal calls.
    AddrToMethodHandleMap* disAddrToMethodHandleMap;
    AddrToMethodHandleMap* GetAddrToMethodHandleMap();

    // Map of instruction addresses to call target method handles for JIT helper calls.
    AddrToMethodHandleMap* disHelperAddrToMethodHandleMap;
    AddrToMethodHandleMap* GetHelperAddrToMethodHandleMap();

    // Map of relocation addresses to relocation target.
    AddrToAddrMap* disRelocationMap;
    AddrToAddrMap* GetRelocationMap();

    const char* disGetMethodFullName(size_t addr);

    FILE* disAsmFile;

    Compiler* disComp;

    bool disDiffable; // 'true' if the output should be diffable (hide or obscure absolute addresses)

    template <typename T>
    T dspAddr(T addr)
    {
        return (addr == 0) ? 0 : (disDiffable ? T(0xD1FFAB1E) : addr);
    }

    /* Callbacks from msdis */

    static size_t __stdcall disCchAddr(
        const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp);

    size_t disCchAddrMember(
        const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp);

    static size_t __stdcall disCchFixup(const DIS*                   pdis,
                                        DIS::ADDR                    addr,
                                        size_t                       size,
                                        __in_ecount(cchMax) wchar_t* wz,
                                        size_t                       cchMax,
                                        DWORDLONG*                   pdwDisp);

    size_t disCchFixupMember(const DIS*                   pdis,
                             DIS::ADDR                    addr,
                             size_t                       size,
                             __in_ecount(cchMax) wchar_t* wz,
                             size_t                       cchMax,
                             DWORDLONG*                   pdwDisp);

    static size_t __stdcall disCchRegRel(
        const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp);

    size_t disCchRegRelMember(
        const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp);

    static size_t __stdcall disCchReg(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax);

    size_t disCchRegMember(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax);

    /* Disassemble helper */

    size_t CbDisassemble(DIS*        pdis,
                         size_t      offs,
                         DIS::ADDR   addr,
                         const BYTE* pb,
                         size_t      cbMax,
                         FILE*       pfile,
                         bool        findLabels,
                         bool        printit       = false,
                         bool        dispOffs      = false,
                         bool        dispCodeBytes = false);
};

/*****************************************************************************/
#endif // LATE_DISASM
/*****************************************************************************/
#endif // _DIS_H_
/*****************************************************************************/
